/**
 * 
 */
package ua.lviv.franko.solvers.stressanalysis.oneDimention;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

import ua.lviv.franko.integration.ConstantFunction1D;
import ua.lviv.franko.integration.ConstantFunction2D;
import ua.lviv.franko.integration.IFunction1D;
import ua.lviv.franko.integration.IFunction2D;
import ua.lviv.franko.matrix.RowType;
import ua.lviv.franko.matrix.hash.lu.HashMatrixLUStyle;
import ua.lviv.franko.matrix.hash.lu.LUFactorization;
import ua.lviv.franko.mlsFunctions.oneDimension.weightFunctions.CubicSpline;
import ua.lviv.franko.visualisation.NewOkCancelDialog;

/**
 * @author marko
 * 
 */
public class TimoshenkoMembraneSolver {
	public static final int					NUMBER_OF_UNKNOWNS	= 3;

	protected ArrayList<AbstractElement1D>	elementsList;
	protected HashMatrixLUStyle				mainMatrix;
	protected double[]						b;
	protected double[]						result;

	public TimoshenkoMembraneSolver(ArrayList<AbstractElement1D> elementsList) {
		this.elementsList = elementsList;
	}

	public void generate() {
		List<Integer> lastElemetNumbers = elementsList.get(elementsList.size() - 1).getNodesNumbers();
		int N = lastElemetNumbers.get(lastElemetNumbers.size() - 1) + 1;
		mainMatrix = new HashMatrixLUStyle(RowType.ARRAY_TYPE, N * NUMBER_OF_UNKNOWNS);
		b = new double[N * NUMBER_OF_UNKNOWNS];

		for (Iterator<AbstractElement1D> iterator = elementsList.iterator(); iterator.hasNext();) {
			AbstractElement1D element = iterator.next();
			// element.generateAll();
			ElementMatrix data = element.generateAll();
			List<Integer> pos = data.getNodeList();
			for (int i = 0; i < pos.size(); i++) {
				for (int j = 0; j < pos.size(); j++) {
					int pos1 = NUMBER_OF_UNKNOWNS * pos.get(i);
					int pos2 = NUMBER_OF_UNKNOWNS * pos.get(j);
					mainMatrix.setValue(pos1, pos2, mainMatrix.getValue(pos1, pos2) + data.matrix[NUMBER_OF_UNKNOWNS * i][NUMBER_OF_UNKNOWNS * j]);
					pos2++;
					mainMatrix.setValue(pos1, pos2, mainMatrix.getValue(pos1, pos2) + data.matrix[NUMBER_OF_UNKNOWNS * i][NUMBER_OF_UNKNOWNS * j + 1]);
					pos2++;
					mainMatrix.setValue(pos1, pos2, mainMatrix.getValue(pos1, pos2) + data.matrix[NUMBER_OF_UNKNOWNS * i][NUMBER_OF_UNKNOWNS * j + 2]);

					pos1 = NUMBER_OF_UNKNOWNS * pos.get(i) + 1;
					pos2 = NUMBER_OF_UNKNOWNS * pos.get(j);
					mainMatrix.setValue(pos1, pos2, mainMatrix.getValue(pos1, pos2) + data.matrix[NUMBER_OF_UNKNOWNS * i + 1][NUMBER_OF_UNKNOWNS * j]);
					pos2++;
					mainMatrix.setValue(pos1, pos2, mainMatrix.getValue(pos1, pos2) + data.matrix[NUMBER_OF_UNKNOWNS * i + 1][NUMBER_OF_UNKNOWNS * j + 1]);
					pos2++;
					mainMatrix.setValue(pos1, pos2, mainMatrix.getValue(pos1, pos2) + data.matrix[NUMBER_OF_UNKNOWNS * i + 1][NUMBER_OF_UNKNOWNS * j + 2]);

					pos1 = NUMBER_OF_UNKNOWNS * pos.get(i) + 2;
					pos2 = NUMBER_OF_UNKNOWNS * pos.get(j);
					mainMatrix.setValue(pos1, pos2, mainMatrix.getValue(pos1, pos2) + data.matrix[NUMBER_OF_UNKNOWNS * i + 2][NUMBER_OF_UNKNOWNS * j]);
					pos2++;
					mainMatrix.setValue(pos1, pos2, mainMatrix.getValue(pos1, pos2) + data.matrix[NUMBER_OF_UNKNOWNS * i + 2][NUMBER_OF_UNKNOWNS * j + 1]);
					pos2++;
					mainMatrix.setValue(pos1, pos2, mainMatrix.getValue(pos1, pos2) + data.matrix[NUMBER_OF_UNKNOWNS * i + 2][NUMBER_OF_UNKNOWNS * j + 2]);
				}
				b[pos.get(i) * NUMBER_OF_UNKNOWNS] += data.vector[NUMBER_OF_UNKNOWNS * i];
				b[pos.get(i) * NUMBER_OF_UNKNOWNS + 1] += data.vector[NUMBER_OF_UNKNOWNS * i + 1];
				b[pos.get(i) * NUMBER_OF_UNKNOWNS + 2] += data.vector[NUMBER_OF_UNKNOWNS * i + 2];
			}
		}

		// for (int i = 0; i < mainMatrix.getN(); i++) {
		// for (int j = 0; j < mainMatrix.getN(); j++) {
		// System.out.print(mainMatrix.getValue(i, j) + "\t");
		// }
		// System.out.println();
		// }
		// System.out.println("======================");
	}

	public double[] getUpdatedBVector(EquationParameters parameters) {
		// TODO implement this
		double[] updatedB = new double[b.length];
		for (Iterator<AbstractElement1D> iterator = elementsList.iterator(); iterator.hasNext();) {
			AbstractElement1D element = iterator.next();
			// element.generateAll();
			ElementMatrix data = element.generateUpdatedVector(parameters);
			List<Integer> pos = data.getNodeList();
			for (int i = 0; i < pos.size(); i++) {
				updatedB[pos.get(i) * NUMBER_OF_UNKNOWNS] += data.vector[NUMBER_OF_UNKNOWNS * i];
				updatedB[pos.get(i) * NUMBER_OF_UNKNOWNS + 1] += data.vector[NUMBER_OF_UNKNOWNS * i + 1];
				updatedB[pos.get(i) * NUMBER_OF_UNKNOWNS + 2] += data.vector[NUMBER_OF_UNKNOWNS * i + 2];
			}
		}
		return updatedB;
	}

	public void setBothDirihlet(double[] left, double[] right) {
		for (int i = 0; i < 3; i++) {
			mainMatrix.getRows().get(i).getValues().clear();
			mainMatrix.getRows().get(i).getIndexes().clear();
			mainMatrix.setValue(i, i, 1.0);
			b[i] = left[i];

			int nnn = mainMatrix.getN();
			mainMatrix.getRows().get(nnn - i - 1).getValues().clear();
			mainMatrix.getRows().get(nnn - i - 1).getIndexes().clear();
			mainMatrix.setValue(nnn - i - 1, nnn - i - 1, 1.0);
			b[nnn - i - 1] = right[2 - i];
		}

	}

	public void setLeftDirihlet(double[] left) {
		for (int i = 0; i < 3; i++) {
			mainMatrix.getRows().get(i).getValues().clear();
			mainMatrix.getRows().get(i).getIndexes().clear();
			mainMatrix.setValue(i, i, 1.0);
			b[i] = left[i];
		}

	}

	public static void setBothDirihlet(HashMatrixLUStyle matrix, double[] b, double[] left, double[] right) {
		for (int i = 0; i < 3; i++) {
			matrix.getRows().get(i).getValues().clear();
			matrix.getRows().get(i).getIndexes().clear();
			matrix.setValue(i, i, 1.0);
			b[i] = left[i];

			int nnn = matrix.getN();
			matrix.getRows().get(nnn - i - 1).getValues().clear();
			matrix.getRows().get(nnn - i - 1).getIndexes().clear();
			matrix.setValue(nnn - i - 1, nnn - i - 1, 1.0);
			b[nnn - i - 1] = left[2 - i];
		}
	}

	public double[] solve() {
		// for (int i = 0; i < mainMatrix.getN(); i++) {
		// for (int j = 0; j < mainMatrix.getN(); j++) {
		// System.out.print(mainMatrix.getValue(i, j) + "\t");
		// }
		// System.out.println();
		// // System.out.println(b[i]);
		//
		// }
		// System.out.println("-------------");
		// int size = mainMatrix.getN();
		// for (int i = 0; i < size; i++) {
		// mainMatrix.setValue(0, i, 0.0);
		// mainMatrix.setValue(1, i, 0.0);
		// mainMatrix.setValue(2, i, 0.0);
		//
		// mainMatrix.setValue(size - 3, i, 0.0);
		// mainMatrix.setValue(size - 2, i, 0.0);
		// mainMatrix.setValue(size - 1, i, 0.0);
		// }
		//
		// mainMatrix.setValue(0, 0, 1.0);
		// mainMatrix.setValue(1, 1, 1.0);
		// mainMatrix.setValue(2, 2, 1.0);
		//
		// mainMatrix.setValue(size - 3, size - 3, 1.0);
		// mainMatrix.setValue(size - 2, size - 2, 1.0);
		// mainMatrix.setValue(size - 1, size - 1, 1.0);
		//
		// double val = 0.0;
		// b[0] = val;
		// b[1] = val;
		// b[2] = val;
		// val = 1.0;
		// b[size - 3] = val;
		// b[size - 2] = val;
		// b[size - 1] = val;

		// for (int i = 0; i < mainMatrix.getN(); i++) {
		// for (int j = 0; j < mainMatrix.getN(); j++) {
		// System.out.print(mainMatrix.getValue(i, j) + "\t");
		// }
		// System.out.println();
		// // System.out.println(b[i]);
		//
		// }
		// System.out.println("-------------");

		LUFactorization lu = new LUFactorization(this.mainMatrix);
		result = lu.calculate(b);
		return result;
	}

	public double[] getB() {
		return b;
	}

	public HashMatrixLUStyle getMainMatrix() {
		return mainMatrix;
	}

	public ArrayList<AbstractElement1D> getElementsList() {
		return elementsList;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		IFunction2D f1 = new ConstantFunction2D(1.0);
		IFunction2D f3 = new ConstantFunction2D(1.0);
		IFunction1D k1 = new ConstantFunction1D(0.0);
		IFunction1D a1 = new ConstantFunction1D(1.0);
		IFunction1D sigma11Plus = new ConstantFunction1D(0.0);
		IFunction1D sigma13Minus = new ConstantFunction1D(0.0);
		IFunction1D sigma11Minus = new ConstantFunction1D(0.0);
		IFunction1D sigma33Minus = new ConstantFunction1D(0.0);
		IFunction1D sigma33Plus = new ConstantFunction1D(0.0);
		IFunction1D sigma13Plus = new ConstantFunction1D(0.0);
		double v = 0.3;
		double e = 10000;
		double h = 0.5;

		double a = 0.0;
		double b = 1.0;

		EquationParameters eqP = new EquationParameters(a1, k1, e, v, h, f1, f3, sigma13Plus, sigma13Minus, sigma11Plus, sigma11Minus, sigma33Plus, sigma33Minus);

		int n = 15;
		double hStep = (b - a) / (double) n;
		ArrayList<AbstractElement1D> elements = new ArrayList<AbstractElement1D>();
		int i = 0;
		for (i = 0; i < n; i++) {
//			elements.add(new FEElementLinear(eqP, i, a + i * hStep, a + (i + 1) * hStep));
//			 elements.add(new BubbleElement(eqP, i, a + i * hStep, a + (i + 1) * hStep));
			 elements.add(new EnrichedLinearElement(eqP, elements.size() > 0 ? elements.get(elements.size() - 1).getNodesNumbers()
			 .get(elements.get(elements.size() - 1).getNodesNumbers().size() - 1) : 0, a + i * hStep, a + (i + 1) * hStep, 5, 1, new CubicSpline(), 2.5));
		}
		long start = System.nanoTime();
		TimoshenkoMembraneSolver s = new TimoshenkoMembraneSolver(elements);
		s.generate();
		s.setBothDirihlet(new double[] { 0.0, 0.0, 0.0 }, new double[] { 0.0, 0.0, 0.0 });
		// s.setLeftDirihlet(new double[]{0.0003,0.0,-0.00132});
		double[] res = s.solve();
//		System.err.println("solve() time is = " + (System.nanoTime() - start));
		// for (int j = 0; j < res.length; j++) {
		// System.out.println(res[j]);
		// }

		XYSeries s1 = new XYSeries("result1", true, true);
		XYSeries s2 = new XYSeries("result2", true, true);
		XYSeries s3 = new XYSeries("result3", true, true);
		XYSeries u1 = new XYSeries("u", true, true);
		XYSeries v1 = new XYSeries("v", true, true);

		// for (int j = 0; j < elements.size() + 1; j++) {
		// s1.add(a + j * hStep, res[j * 3]);
		// s2.add(a + j * hStep, res[j * 3 + 1]);
		// s3.add(a + j * hStep, res[j * 3 + 2]);
		// }

		int NNN = 10;
		AbstractElement1D el = null;
		System.out.println("myplot([");
		for (int j = 0; j < elements.size(); j++) {
			el = elements.get(j);
			double hhh = hStep / NNN;
			for (int k = 0; k < NNN; k++) {
				double x = a + hStep * j + k * hhh;
				double[] tmp = el.calculate(x, res);
				s1.add(x, tmp[0]);
				System.out.println(x + ", " + tmp[2]);
				s2.add(x, tmp[1]);
				s3.add(x, tmp[2]);
				u1.add(x, tmp[1]);
				v1.add(x, tmp[0] + tmp[2] * h / 2.0);
			}
		}
		double[] tmp = el.calculate(b, res);
		s1.add(b, tmp[0]);
		s2.add(b, tmp[1]);
		s3.add(b, tmp[2]);
		u1.add(b, tmp[1]);
		v1.add(b, tmp[0] + tmp[2] * h / 2.0);
		System.out.println(b + ", " + tmp[2]);
		System.out.println("])");
		XYSeriesCollection col = new XYSeriesCollection();
		col.addSeries(s1);
		col.addSeries(s2);
		col.addSeries(s3);
		// col.addSeries(u1);
		// col.addSeries(v1);
		NewOkCancelDialog d = new NewOkCancelDialog(null, false, col);
		d.setLocationRelativeTo(null);
		d.setVisible(true);
	}
}